﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace UIFrameWork
{
    public class UIModule : NodeBase
    {
        private static UIModule _ins;
        public static UIModule Ins
        {
            get
            {
                if (_ins == null)
                    _ins = new UIModule();
                return _ins;
            }
        }

        private Camera _uiCamera;
        private Transform _uiRoot;
        private WindowConfig _windowConfig;

        /// <summary>
        ///  所有窗口字典
        /// </summary>
        private Dictionary<string, WindowBase> _allWindowDic = new Dictionary<string, WindowBase>();
        /// <summary>
        ///  所有窗口列表
        /// </summary>
        private List<WindowBase> _allWindowList = new List<WindowBase>();
        /// <summary>
        ///  所有可见窗口列表
        /// </summary>
        private List<WindowBase> _visibleWindowList = new List<WindowBase>();
        /// <summary>
        ///  队列，用来管理弹窗的循环弹出
        /// </summary>
        private Queue<WindowBase> _windowStack = new Queue<WindowBase>();
        /// <summary>
        ///  开始弹出堆栈的表只，可以用来处理多种情况，比如：正在出栈种有其他界面弹出，可以直接放到栈内进行弹出 等
        /// </summary>
        private bool _startPopStackWndStatus = false;

        public override void OnInit()
        {
            _uiCamera = Camera.main;
            _uiRoot = GameObject.Find("UIRoot").transform;

            //GameObject.DontDestroyOnLoad(_uiCamera.gameObject);
            GameObject.DontDestroyOnLoad(_uiRoot.gameObject);

            _windowConfig = Resources.Load<WindowConfig>("WindowConfig");
#if UNITY_EDITOR
            _windowConfig.GeneratorWindowConfig();
#endif
        }


        public override void OnUpdate()
        {
            base.OnUpdate();

            for (int i = 0; i < _visibleWindowList.Count; i++)
            {
                if (_visibleWindowList[i].needUpdate)
                {
                    _visibleWindowList[i].OnUpdate();
                }
            }
        }
        #region 开放功能
        /// <summary>
        ///  摄像机更换时设置摄像机
        /// </summary>
        /// <param name="camera"></param>
        public void SetCanvasCamera(Camera camera)
        {
            _uiCamera = camera;
            for (int i = 0; i < _allWindowList.Count; i++)
            {
                //_allWindowList[i].canvas.worldCamera = _uiCamera;
                var canvasRect = _allWindowList[i].transform.GetComponent<RectTransform>();
                canvasRect.anchorMax = new Vector2(0.5f, 0.5f);
                canvasRect.anchorMin = new Vector2(0.5f, 0.5f);
                canvasRect.pivot = new Vector2(0.5f, 0.5f);
                canvasRect.anchoredPosition = Vector2.zero;
                canvasRect.sizeDelta = new Vector2(720, 1600);

                _allWindowList[i].transform.GetComponent<Canvas>().overrideSorting = true;

            }
            var canvas = _uiRoot.GetComponent<Canvas>();

            canvas.worldCamera = _uiCamera;
            //var canvasRect = canvas.transform.GetComponent<RectTransform>();
            //canvasRect.anchoredPosition = Vector2.zero;
            //canvasRect.sizeDelta = new Vector2(720, 1600);
        }
        /// <summary>
        ///  查询窗口是否弹出
        /// </summary>
        /// <param name="wndName"></param>
        /// <returns></returns>
        public bool IsWindowShow(string wndName)
        {
            return _visibleWindowList.Any(d => d.name == wndName);
        }
        /// <summary>
        ///  获取弹出的窗口
        /// </summary>
        /// <returns></returns>
        public IEnumerable<WindowBase> GetOpeningWindows()
        {
            foreach (var form in _visibleWindowList)
                yield return form;
        }
        /// <summary>
        ///  查询窗口是否被缓存
        /// </summary>
        /// <param name="wndName"></param>
        /// <returns></returns>
        public bool HasBufferWindow(string wndName)
        {
            return _allWindowList.Any(d => d.name == wndName);
        }
        /// <summary>
        ///  反射预加载
        /// </summary>
        /// <param name="uis"></param>
        public void PreLoadWindow(List<(string, GameObject)> uis)
        {
            var methodInfo = typeof(UIModule).GetMethod("PreLoadWindow", new Type[] { typeof(GameObject) });

            // 加载 UI 到缓存
            for (int i = 0; i < uis.Count; i++)
            {
                Type type = Type.GetType($"UIFrameWork.{uis[i].Item1}");
                var genericMethod = methodInfo.MakeGenericMethod(type);
                genericMethod.Invoke(UIModule.Ins, new object[] { uis[i].Item2 });
            }
        }
        /// <summary>
        ///  反射预加载
        /// </summary>
        /// <param name="wndName"></param>
        /// <param name="window"></param>
        public void PreLoadWindow(string wndName, GameObject window)
        {
            var methodInfo = typeof(UIModule).GetMethod("PreLoadWindow", new Type[] { typeof(GameObject) });
            Type type = Type.GetType($"UIFrameWork.{wndName}");
            var genericMethod = methodInfo.MakeGenericMethod(type);
            genericMethod.Invoke(UIModule.Ins, new object[] { window });
        }
        #endregion

        #region 窗口管理
        /// <summary>
        /// 只加载物体，不调用生命周期
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void PreLoadWindow<T>(GameObject window = null) where T : WindowBase, new()
        {
            System.Type type = typeof(T);
            string wndName = type.Name;
            T windowBase = new T();
            //克隆界面，初始化界面信息
            //1.生成对应的窗口预制体
            GameObject wnd = TempLoadWindow(wndName, window);
            //2.初始出对应管理类
            if (wnd != null)
            {
                windowBase.gameObject = wnd;
                windowBase.transform = wnd.transform;
                windowBase.canvas = wnd.GetComponent<Canvas>();
                windowBase.canvas.worldCamera = _uiCamera;
                windowBase.name = wnd.name;
                //windowBase.OnAwake();
                windowBase.SetVisible(false);
                RectTransform rectTrans = wnd.GetComponent<RectTransform>();
                rectTrans.anchorMax = Vector2.one;
                rectTrans.offsetMax = Vector2.zero;
                rectTrans.offsetMin = Vector2.zero;
                _allWindowDic.Add(wndName, windowBase);
                _allWindowList.Add(windowBase);
                Debug.Log("预加载窗口 窗口名字：" + wndName);
            }
            else
            {
                Debug.LogError("预加载窗口错误 窗口名字：" + wndName);
            }
        }
        /// <summary>
        ///  打开窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T PopUpWindow<T>() where T : WindowBase, new()
        {
            System.Type type = typeof(T);
            string wndName = type.Name;
            WindowBase wnd = GetWindow(wndName);
            if (wnd != null)
            {
                return ShowWindow(wndName) as T;
            }

            T t = new T();
            return InitWindow(t, wndName) as T;
        }
        /// <summary>
        ///  获取窗口对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        public T GetWindow<T>() where T : WindowBase
        {
            System.Type type = typeof(T);
            string wndName = type.Name;
            var tmp = GetWindow(wndName);
            if (tmp != null)
                return tmp as T;
            else
            {
                Debug.LogError("该窗口没有获取到：" + wndName);
                return null;
            }
        }
        /// <summary>
        ///  隐藏窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void HideWindow<T>() where T : WindowBase
        {
            HideWindow(typeof(T).Name);
        }
        /// <summary>
        ///  销毁窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void DestroyWindow<T>() where T : WindowBase
        {
            DestroyWindow(typeof(T).Name);
        }
        /// <summary>
        ///  销毁所有窗口
        /// </summary>
        /// <param name="filterList"></param>
        public void DestroyAllWindow(List<string> filterList = null)
        {
            for (int i = _allWindowList.Count - 1; i >= 0; i--)
            {
                var window = _allWindowList[i];
                if (window == null || (filterList != null && filterList.Contains(window.name)))
                    continue;
                DestroyWindow(window);
            }
            Resources.UnloadUnusedAssets();
        }

        private WindowBase InitWindow(WindowBase windowBase, string wndName)
        {
            var newWnd = TempLoadWindow(wndName);
            if (newWnd != null)
            {
                windowBase.gameObject = newWnd;
                windowBase.transform = newWnd.transform;
                windowBase.canvas = newWnd.GetComponent<Canvas>();
                windowBase.canvas.worldCamera = _uiCamera;
                windowBase.name = wndName;
                windowBase.transform.SetAsLastSibling();
                windowBase.OnAwake();
                windowBase.SetVisible(true);
                windowBase.OnShow();
                var rectTrans = newWnd.GetComponent<RectTransform>();
                rectTrans.anchorMax = Vector2.zero;
                rectTrans.offsetMax = Vector2.zero;
                rectTrans.offsetMin = Vector2.zero;
                _allWindowDic.Add(wndName, windowBase);
                _allWindowList.Add(windowBase);
                _visibleWindowList.Add(windowBase);
                SetWindowMaskVisible();
                return windowBase;
            }
            Debug.LogError("没有加载到对应的窗口，窗口名字：" + wndName);
            return null;
        }
        private WindowBase PopUpWindow(WindowBase window)
        {
            System.Type type = window.GetType();
            string wndName = type.Name;
            WindowBase wnd = GetWindow(wndName);
            if (wnd != null)
            {
                return ShowWindow(wndName);
            }
            return InitWindow(window, wndName);
        }
        public WindowBase ShowWindow(string winName)
        {
            WindowBase window = null;
            if (_allWindowDic.ContainsKey(winName))
            {
                window = _allWindowDic[winName];
                if (window.gameObject != null && window.visible == false)
                {
                    // 强制设置大小
                    var canvasRect = window.transform.GetComponent<RectTransform>();
                    canvasRect.anchorMax = new Vector2(0.5f, 0.5f);
                    canvasRect.anchorMin = new Vector2(0.5f, 0.5f);
                    canvasRect.pivot = new Vector2(0.5f, 0.5f);
                    canvasRect.anchoredPosition = Vector2.zero;
                    canvasRect.sizeDelta = new Vector2(720, 1600);

                    window.transform.GetComponent<Canvas>().overrideSorting = true;

                    _visibleWindowList.Add(window);
                    window.transform.SetAsLastSibling();
                    if (window.isAwake == false)
                        window.OnAwake();
                    window.SetVisible(true);
                    SetWindowMaskVisible();
                    window.OnShow();
                }
                return window;
            }
            else
            {
                Debug.LogError(winName + "窗口不存在，请调用 PopUpWindow 进行弹出");
                return null;
            }
        }
        public void HideWindow(string name)
        {
            var wnd = GetWindow(name);
            HideWindow(wnd);
        }
        private void HideWindow(WindowBase window)
        {
            if (window != null && window.visible)
            {
                _visibleWindowList.Remove(window);
                window.SetVisible(false);
                SetWindowMaskVisible();
                window.OnHide();
            }
            //在出栈的情况下，上一个界面隐藏时，自动打开栈种的下一个界面
            PopNextStackWindow(window);
        }
        private void DestroyWindow(string name)
        {
            var wnd = GetWindow(name);
            DestroyWindow(wnd);
        }
        private void DestroyWindow(WindowBase window)
        {
            if (window != null)
            {
                if (_allWindowDic.ContainsKey(window.name))
                {
                    _allWindowDic.Remove(window.name);
                    _allWindowList.Remove(window);
                }
                HideWindow(window);
                window.OnDestroy();
                GameObject.Destroy(window.gameObject);
            }
            //在出栈的情况下，上一个界面销毁时，自动打开栈种的下一个界面
            PopNextStackWindow(window);
        }
        private WindowBase GetWindow(string name)
        {
            if (_allWindowDic.ContainsKey(name))
            {
                return _allWindowDic[name];
            }
            return null;
        }
        /// <summary>
        ///  确定渲染的Mask
        /// </summary>
        private void SetWindowMaskVisible()
        {
            if (!UISetting.Ins.SINGMASK_SYSTEM)
            {
                return;
            }
            // 最大渲染层级的窗口
            WindowBase maxOrderWndBase = null;
            // 最大渲染层级
            int maxOrder = 0;
            // 最大排序下标 在相同父节点下的位置下标
            int maxIndex = 0;

            for (int i = 0; i < _visibleWindowList.Count; i++)
            {
                var window = _visibleWindowList[i];
                if (window != null && window.gameObject != null)
                {
                    window.SetMaskViseble(false);
                    if (maxOrderWndBase == null)
                    {
                        maxOrderWndBase = window;
                        maxOrder = window.canvas.sortingOrder;
                        maxIndex = window.transform.GetSiblingIndex();
                    }
                    else
                    {
                        // 获取最大渲染层级的窗口
                        if (maxOrder < window.canvas.sortingOrder)
                        {
                            maxOrderWndBase = window;
                            maxOrder = window.canvas.sortingOrder;
                        }
                        // 如果渲染层级一样，则由节点层级是否更靠下决定
                        else if (maxOrder == window.canvas.sortingOrder &&
                                maxIndex < window.transform.GetSiblingIndex())
                        {
                            maxOrderWndBase = window;
                            maxIndex = window.transform.GetSiblingIndex();
                        }
                    }
                }
            }
            if (maxOrderWndBase != null)
            {
                maxOrderWndBase.SetMaskViseble(true);
            }
        }
        /// <summary>
        ///  加载预制体，更改该方法可更换加载方法
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private GameObject TempLoadWindow(string name, GameObject win = null)
        {
            GameObject tmp;
            if (win != null)
                tmp = GameObject.Instantiate<GameObject>(win, _uiRoot);
            else
                tmp = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>(_windowConfig.GetWindowPath(name)), _uiRoot);
            tmp.transform.localPosition = Vector3.zero;
            tmp.transform.rotation = Quaternion.identity;
            //tmp.transform.localScale = new Vector3(0.006410256f, 0.006410256f, 0.006410256f);

            var canvasRect = tmp.transform.GetComponent<RectTransform>();
            canvasRect.anchorMax = new Vector2(0.5f, 0.5f);
            canvasRect.anchorMin = new Vector2(0.5f, 0.5f);
            canvasRect.pivot = new Vector2(0.5f, 0.5f);
            canvasRect.anchoredPosition = Vector2.zero;
            canvasRect.sizeDelta = new Vector2(720, 1600);

            var canvas = tmp.transform.GetComponent<Canvas>();
            canvas.overrideSorting = true;

            tmp.name = name;
            return tmp;
        }
        #endregion

        #region 堆栈系统

        /// <summary>
        /// 进栈一个界面
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="popCallBack"></param>
        public void PushWindowToStack<T>(Action<WindowBase> popCallBack = null) where T : WindowBase, new()
        {
            T wndBase = new T();
            wndBase.PopStackListener = popCallBack;
            _windowStack.Enqueue(wndBase);
        }
        /// <summary>
        /// 弹出堆栈中第一个弹窗
        /// </summary>
        public void StartPopFirstStackWindow()
        {
            if (_startPopStackWndStatus) return;
            _startPopStackWndStatus = true;//已经开始进行堆栈弹出的流程，
            PopStackWindow();
        }
        /// <summary>
        /// 压入并且弹出堆栈弹窗
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="popCallBack"></param>
        public void PushAndPopStackWindow<T>(Action<WindowBase> popCallBack = null) where T : WindowBase, new()
        {
            PushWindowToStack<T>(popCallBack);
            StartPopFirstStackWindow();
        }
        /// <summary>
        /// 弹出堆栈中的下一个窗口
        /// </summary>
        /// <param name="windowBase"></param>
        private void PopNextStackWindow(WindowBase windowBase)
        {
            if (windowBase != null && _startPopStackWndStatus && windowBase.PopStack)
            {
                windowBase.PopStack = false;
                PopStackWindow();
            }
        }
        /// <summary>
        /// 弹出堆栈弹窗
        /// </summary>
        /// <returns></returns>
        public bool PopStackWindow()
        {
            if (_windowStack.Count > 0)
            {
                WindowBase window = _windowStack.Dequeue();
                WindowBase popWindow = PopUpWindow(window);
                popWindow.PopStackListener = window.PopStackListener;
                popWindow.PopStack = true;
                popWindow.PopStackListener?.Invoke(popWindow);
                popWindow.PopStackListener = null;
                return true;
            }
            else
            {
                _startPopStackWndStatus = false;
                return false;
            }
        }
        public void ClearStackWindows()
        {
            _windowStack.Clear();
        }
        #endregion
    }
}


